﻿/* Copyright (c) Microsoft Corporation
 * 
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * 
 * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
 * 
 * See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Windows.Threading;
using System.Net;
using System.Diagnostics;
using System.Threading;

namespace ProximityTapper
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        UdpNfcSimBroadcastListener udpListener = new UdpNfcSimBroadcastListener();

        public MainWindow()
        {
            InitializeComponent();

            // Initialize our device discovery listener
            udpListener.DeviceDiscovered += DeviceDiscovered;
            udpListener.Start();

            try
            {
                if (ProximityTapper.Properties.Settings.Default.SavedDevices == null)
                {
                    ProximityTapper.Properties.Settings.Default.SavedDevices = new System.Collections.Specialized.StringCollection();
                }
                else
                {
                    foreach (var p in ProximityTapper.Properties.Settings.Default.SavedDevices)
                    {
                        AddDevice(new ProximityDeviceConnectInfo(p), false);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Execption in startup: " + ex.ToString());
            }
        }

        ~MainWindow()
        {
            var c = new System.Collections.Specialized.StringCollection();
            lock (dgDevices)
            {
                foreach (ProximityDeviceConnectInfo p in dgDevices.Items)
                {
                    c.Add(p.ToString());
                }
            }
            ProximityTapper.Properties.Settings.Default.SavedDevices = c;
            ProximityTapper.Properties.Settings.Default.Save();
        }

        void DeviceDiscovered(ProximityDeviceConnectInfo found)
        {
            bool showDiscovered = false;

            chkShowDiscoveredDevices.Dispatcher.Invoke((Action)(() =>
            {
                showDiscovered = (chkShowDiscoveredDevices.IsChecked == true);
            }));

            lock (dgDevices)
            {
                var o = (from ProximityDeviceConnectInfo i in dgDevices.Items where found.Matches(i) select i).FirstOrDefault();

                if (o != null)
                {
                    Debug.WriteLine(found.MacAddress + " " + found.IpAddress + " " + DateTime.Now);
                    o.Update(found);
                    o.LastPing = DateTime.Now;
                }
                else if (showDiscovered == true && found.IpAddress.StartsWith("169.254."))
                {
                    Debug.WriteLine(found.MacAddress + " " + found.IpAddress + " " + DateTime.Now);
                    AddDevice(found, false);
                }
            }
        }

        private void btnAddDevice_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(txtDeviceIP.Text))
            {
                return;
            }
            var p = new ProximityDeviceConnectInfo(txtDeviceIP.Text);

            AddDevice(p, true);
            
            txtDeviceIP.Text = "";
        }

        private void AddDevice(ProximityDeviceConnectInfo p, bool saveDevice)
        {
            bool found = false;

            lock (dgDevices)
            {
                foreach (ProximityDeviceConnectInfo ip in dgDevices.Items)
                {
                    if (ip.Matches(p))
                    {
                        ip.Update(p);
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    dgDevices.Dispatcher.Invoke((Action)(() =>
                    {
                        dgDevices.Items.Add(p);
                    }));
                    if (saveDevice)
                    {
                        ProximityTapper.Properties.Settings.Default.SavedDevices.Add(txtDeviceIP.Text);
                        ProximityTapper.Properties.Settings.Default.Save();
                    }
                }
            }
        }

        private void btnTapDevices_Click(object sender, RoutedEventArgs e)
        {
            if (dgDevices.SelectedItems.Count > 2)
            {
                MessageBox.Show("Maximum of 2 devices may be tapped together");
                return;
            }

            CrossoverConnection c = new CrossoverConnection();

            lock (dgDevices)
            {
                foreach (ProximityDeviceConnectInfo device in dgDevices.SelectedItems)
                {
                    if (!string.IsNullOrEmpty(device.IpAddress))
                    {
                        c.AddConnection(device);
                    }
                    else
                    {
                        c.ClearConnections();
                        MessageBox.Show("Failed to tap, at least one device does not have an IP address (ensure the device is on and broadcasting TShell packets, and wait for the IP address to appear in the devices list)");
                        return;
                    }
                }
                lstConnections.Items.Add(c);
                lstConnections.SelectedItem = c;
            }
        }

        private void btnUntap_Click(object sender, RoutedEventArgs e)
        {
            List<object> toRemove = new List<object>();
            foreach (CrossoverConnection c in lstConnections.SelectedItems)
            {
                c.ClearConnections();
                toRemove.Add(c);
            }

            foreach (var o in toRemove)
            {
                lstConnections.Items.Remove(o);
            }
        }

        private void btnRemoveDevice_Click(object sender, RoutedEventArgs e)
        {
            lock (dgDevices)
            {
                while (dgDevices.SelectedItems.Count > 0)
                {
                    dgDevices.Items.Remove(dgDevices.SelectedItem);
                }
            }
        }

        private void btnSendURL_Click(object sender, RoutedEventArgs e)
        {
            string strUrl = Microsoft.VisualBasic.Interaction.InputBox("Enter URL to send", "", "http://www.bing.com");

            if (!string.IsNullOrEmpty(strUrl))
            {
                foreach (CrossoverConnection c in lstConnections.SelectedItems)
                {
                    c.SendStringMessage("WindowsUri", strUrl, false);
                }
            }
        }

        private void btnSendString_Click(object sender, RoutedEventArgs e)
        {
            string type = Microsoft.VisualBasic.Interaction.InputBox("Enter message type");
            if (type != "")
            {
                string data = Microsoft.VisualBasic.Interaction.InputBox("Enter data");

                if (type != "" && data != "")
                {
                    foreach (CrossoverConnection c in lstConnections.SelectedItems)
                    {
                        c.SendStringMessage(type, data, true);
                    }
                }
            }
        }

        private void btnSendFile_Click(object sender, RoutedEventArgs e)
        {
            string type = Microsoft.VisualBasic.Interaction.InputBox("Enter message type");

            if (type != "")
            {
                Microsoft.Win32.OpenFileDialog openDialog = new Microsoft.Win32.OpenFileDialog();
                if (openDialog.ShowDialog(this) == true)
                {
                    foreach (CrossoverConnection c in lstConnections.SelectedItems)
                    {
                        try
                        {
                            c.SendBinaryMessage(type, System.IO.File.ReadAllBytes(openDialog.FileName));
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Failed to send: " + ex.ToString());
                        }
                    }
                }
            }
        }

        private void lstConnections_SelectionChanged(object sender, EventArgs e)
        {
            if (lstConnections.SelectedItem != null)
            {
                dgMessages.ItemsSource = ((CrossoverConnection)lstConnections.SelectedItem).Messages;
            }
            else
            {
                dgMessages.ItemsSource = null;
            }
        }

        private void btnTapUntapDevices_Click(object sender, RoutedEventArgs e)
        {
            btnTapDevices_Click(null, null);
            DispatcherTimer t = new DispatcherTimer();
            t.Interval = new TimeSpan(0, 0, 1);
            t.Tick += (s, e2) => { btnUntap_Click(null, null); t.Stop(); };
            t.Start();
        }

        private void mnuResendMessageToAllTargets_Click(object sender, RoutedEventArgs e)
        {
            foreach (ProximityMessage m in dgMessages.SelectedItems)
            {
                var newMsg = new ProximityMessage(m.ToPacketBytes(), m.SourceDevice, m.Connection);
                foreach (var d in m.TargetDevices)
                {
                    m.Connection.SendMessage(newMsg, d);
                }
            }
        }

        private void mnuRemoveDevice_Click(object sender, RoutedEventArgs e)
        {
            btnRemoveDevice_Click(null, null);
        }

        private void mnuCopyPayloadAsHex_Click(object sender, RoutedEventArgs e)
        {
            if (dgMessages.SelectedItem == null)
            {
                return;
            }
            var m = (ProximityMessage)dgMessages.SelectedItem;
            StringBuilder hex = new StringBuilder(m.Data.Length * 6);

            foreach (byte b in m.Data)
            {
                hex.Append(string.Format("0x{0:X2}, ", b));
            }

            Clipboard.SetText(hex.ToString());
        }

        private void mnuRenameDevice_Click(object sender, RoutedEventArgs e)
        {
            lock (dgDevices)
            {
                if (dgDevices.SelectedItems.Count == 1)
                {
                    string name = Microsoft.VisualBasic.Interaction.InputBox("Enter new name", "Rename Device", "");

                    if (!string.IsNullOrWhiteSpace(name))
                    {
                        var device = (ProximityDeviceConnectInfo)dgDevices.SelectedItem;
                        device.Name = name;
                    }

                }
            }
        }
    }
}
